#include "StdAfx.h"
#include "CourtModel.h"

using namespace std;
using namespace cv;


//Reads the text file that specifies the court model with lines, intersection points and the lines that they belong to. See "model.txt" for the actual file.


//a comparator used to sort lines.
struct model_linecompare {
	bool operator() (Line a, Line b) { 
		vector<Point> aPts = a.getPoints();
		vector<Point> bPts = b.getPoints();
		int aVal, bVal;

		//comparing horizontal lines. If 2 lines has the same y value, use x values to sort.
		if(a.isHorizontal()) {
			aVal = aPts[0].y;
			bVal = bPts[0].y;
			if((aPts[0].x == bPts[1].x && aPts[0].y == bPts[1].y) || (aPts[1].x == bPts[0].x && aPts[1].y == bPts[0].y)){
				aVal = aPts[0].x;
				bVal = bPts[0].x; 
			}

		}
		//comparing vertical lines. If 2 lines has the same x value, use y values to sort.
		else {
			aVal = aPts[0].x;
			bVal = bPts[0].x;
			if((aPts[0].x == bPts[1].x && aPts[0].y == bPts[1].y) || (aPts[1].x == bPts[0].x && aPts[1].y == bPts[0].y)){
				aVal = aPts[0].y;
				bVal = bPts[0].y; 
			}
		}
		return aVal < bVal;
	}
} model_comparitor;


//constructor of CourtModel. Takes in the model txt file and double the length and width.
CourtModel::CourtModel(string modelFile) {
	readModel(modelFile);
	rows = 624;
	cols = 288;
	courtAspectRatio = rows*1.0/cols;
	courtArea = rows * cols;
	expandModelLines();
}

//parsing the model txtFile
void CourtModel::readModel(string modelFile) {

	ifstream file;
	file.open(modelFile);
	string line;

	if(!file.is_open() && !file.good()) {
		return;
	}

	//Read Lines Line format: point1.x point1.y point2.x point2.y lineNum
	for(size_t i = 0; i < 10; i++) {
		getline(file, line, ' ');
		int x1 = atoi(line.c_str());

		getline(file, line, ' ');
		int y1 = atoi(line.c_str());

		getline(file, line, ' ');
		int x2 = atoi(line.c_str());

		getline(file, line,' ');
		int y2 = atoi(line.c_str());

		getline(file, line);
		int id = atoi(line.c_str());

		Point linePt1(x1*2,y1*2);
		Point linePt2(x2*2, y2*2);
		Line tempLine(linePt1, linePt2);

		if(tempLine.isVertical()) {
			_verticalLines.push_back(tempLine);
		}
		else if(tempLine.isHorizontal()) {
			_horizontalLines.push_back(tempLine);
		}
		allLines.push_back(tempLine);
		_courtLines.push_back(tempLine);
	}

	//Read intersection Pts. Intersection points format: point.x point.y firstLine secondLine
	for(size_t i = 0; i < 20; i++) {
		getline(file, line, ' ');
		int x = atoi(line.c_str());
		getline(file, line, ' ');
		int y = atoi(line.c_str());
		getline(file, line, ' ');
		int hl = atoi(line.c_str()) - 1;
		Line horLine = _courtLines[hl];

		getline(file, line);
		int vl = atoi(line.c_str()) - 1;
		Line verLine = _courtLines[vl];

		Point iPt(x*2,y*2);
		//Intersection intsec(horLine, verLine, iPt);
		Intersection intsec(iPt);
		_intersections.push_back(intsec);
		_intersectionPoints.push_back(iPt);
	}

	file.close();
}

vector<Line> CourtModel::getHorLines() {
	return _horizontalLines;
}

vector<Line> CourtModel::getVerLines() {
	return _verticalLines;
}

vector<Point> CourtModel::getIntSecs() {
	return _intersectionPoints;
}

//Draws the model in a Mat.
void CourtModel::drawModel(Mat m) {
	for(size_t i = 0; i < _courtLines.size(); i++) {
		Line l = _courtLines[i];
		l.drawLine(m);
	}
	for(size_t i = 0; i < _intersectionPoints.size(); i++) {
		Point pt = _intersectionPoints[i];
		circle(m, pt, 4, Scalar(0,255,0));
	}
}


//gets the bounding lines of the court.
vector<Line> CourtModel::findBoundingLines() 
{
	vector<Line> lines(4);
	Line maxHor = findMaxHorzLine(_horizontalLines);
	Line minHor = findMinHorzLine(_horizontalLines);

	Line maxVer = findMaxVertLine(_verticalLines);
	Line minVer = findMinVertLine(_verticalLines);

	lines[0] = minHor;
	lines[1] = maxHor;
	lines[2] = minVer;
	lines[3] = maxVer;

	return lines;
}

Line CourtModel::findMinVertLine(vector<Line> lines) {
	Line minLine;
	int minX = std::numeric_limits<int>::max();

	for(size_t i = 0; i < lines.size(); i++) {
		Line currLine = lines[i];

		if(currLine.isVertical()) {

			vector<Point> points = currLine.getPoints();
			Point p1 = points[0];
			Point p2 = points[1];

			int currMin = min(p1.x, p2.x);
			if(currMin < minX) {
				minLine = currLine;
				minX = currMin;
			}
		}
	}
	return minLine;
}

Line CourtModel::findMaxVertLine(vector<Line> lines) {
	Line maxLine;
	int maxX = std::numeric_limits<int>::min();

	for(size_t i = 0; i < lines.size(); i++) {
		Line currLine = lines[i];

		if(currLine.isVertical()) {
			vector<Point> points = currLine.getPoints();
			Point p1 = points[0];
			Point p2 = points[1];

			int currMax = max(p1.x, p2.x);
			if(currMax > maxX) {
				maxLine = currLine;
				maxX = currMax;
			}
		}
	}
	return maxLine;
}

Line CourtModel::findMinHorzLine(vector<Line> lines) {
	Line minLine;
	int minX = std::numeric_limits<int>::max();

	for(size_t i = 0; i < lines.size(); i++) {
		Line currLine = lines[i];

		if(currLine.isHorizontal()) {

			vector<Point> points = currLine.getPoints();
			Point p1 = points[0];
			Point p2 = points[1];

			int currMin = min(p1.y, p2.y);
			if(currMin < minX) {
				minLine = currLine;
				minX = currMin;
			}
		}
	}
	return minLine;
}

Line CourtModel::findMaxHorzLine(vector<Line> lines) {
	Line maxLine;
	int maxX = std::numeric_limits<int>::min();

	for(size_t i = 0; i < lines.size(); i++) {
		Line currLine = lines[i];

		if(currLine.isHorizontal()) {
			vector<Point> points = currLine.getPoints();
			Point p1 = points[0];
			Point p2 = points[1];

			int currMax = max(p1.y, p2.y);
			if(currMax > maxX) {
				maxLine = currLine;
				maxX = currMax;
			}
		}
	}
	return maxLine;
}


// expand the lines to include segments of the court. e.g. the baseline is expanded to 3 segments.
void CourtModel::expandModelLines(){
	
	
	// find intersection of all horizontal lines and vertical lines.
	for(size_t hor = 0; hor < _horizontalLines.size(); hor++) {
		Line& hLine = _horizontalLines[hor];
		
		if(hLine.getPoints()[0].y == rows/2) 	continue; //ignoring the middle horizontal line as it's never detected in frames.
		
		for(size_t ver = 0; ver < _verticalLines.size(); ver++) {
			Line& vLine = _verticalLines[ver];
			if(vLine.getPoints()[0].x == cols/2) 	continue; //ignoring the middle vertical line as it does not run through the whole court.

			Point intersection = hLine.findIntersection(vLine);		
			Intersection intsec(intersection);
			hLine.addIntersection(intsec);
			vLine.addIntersection(intsec);
			
		}
	}

	//creating horizontal line segments based on vertical line intersections
	int linesCount = _horizontalLines.size();
	for(size_t hor = 0; hor <linesCount ; hor++) {
		Line& hLine = _horizontalLines[hor];
		vector<Intersection> inters  = hLine.getIntersectionPts();
		if (inters.size() ==0) continue;
		Point startPoint = inters[0].intSecPt;
		Point endPoint;
		for (int i=1; i<inters.size(); i++){
			endPoint = inters[i].intSecPt;
			Line tempLine(startPoint, endPoint);
			_segmentHorizontalLines.push_back(tempLine);
			//_horizontalLines.push_back(tempLine);
			startPoint = endPoint;
		}
	}

	//creating vertical line segments based on vertical line intersections
	linesCount = _verticalLines.size();
	for(size_t ver = 0; ver < linesCount; ver++) {
		Line& vLine = _verticalLines[ver];
		vector<Intersection> inters  = vLine.getIntersectionPts();
		if (inters.size() ==0) continue;
		Point startPoint = inters[0].intSecPt;
		Point endPoint;
		for (int i=1; i<inters.size(); i++){
			endPoint = inters[i].intSecPt;
			Line tempLine(startPoint, endPoint);
			_segmentVerticalLines.push_back(tempLine);
			//_verticalLines.push_back(tempLine);
			startPoint = endPoint;
		}
	}
	
	//sort the lines so they can be accessed in order later.
	sort(_segmentHorizontalLines.begin(), _segmentHorizontalLines.end(), model_comparitor);
	sort(_segmentVerticalLines.begin(), _segmentVerticalLines.end(), model_comparitor);
}

vector<Line> CourtModel::getSegmentVerLines(){
	return _segmentVerticalLines;
}
vector<Line> CourtModel::getSegmentHorLines(){
	return _segmentHorizontalLines;
}




CourtModel::CourtModel()
{
}


CourtModel::~CourtModel()
{
}